<?php

declare(strict_types=1);

namespace App\Middleware;

use App\JsonRpc\AuthServiceInterface;
use App\JsonRpc\UserServiceInterface;
use Hyperf\Codec\Json;
use Hyperf\Context\ApplicationContext;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Context\Context;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Hyperf\Di\Annotation\Inject;
use function Hyperf\Support\env;
use Hyperf\HttpServer\Contract\RequestInterface as HttpRequestInterface;

/**
 * 登录验证中间件
 */
class AuthMiddleware implements MiddlewareInterface
{
    /**
     * @var $config 配置对象
     */
    protected $config;

    /**
     * @var $container 容器对象
     */
    protected $container;

    #[Inject]
    protected AuthServiceInterface $authService;

    #[Inject]
    protected UserServiceInterface $userService;

    /**
     * @var $http_code_config http状态码配置
     */
    protected $http_code_config;

    public function __construct()
    {
        $this->container = ApplicationContext::getContainer();
        $this->config = $this->container->get(ConfigInterface::class);
        $this->http_code_config = $this->config->get("http_code");
    }

    /**
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function process(ServerRequestInterface $request,RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $this->container->get(\Hyperf\HttpServer\Contract\ResponseInterface::class);
        // 获取请求头数据
        $header = $request->getHeaders();
        // 获取token
        $token = isset($header["authorization"][0]) ? trim($header["authorization"][0]) : '';
        if(! $token){
            return $this->resqMake($response,"Token 未传", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        // 分拆TOKEN
        $tokenArr = explode(' ',$token);
        // 验证Token
        if(trim($tokenArr[0]) != "Bearer"){
            return $this->resqMake($response,"Token前缀错误", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        if(!isset($tokenArr[1])){
            return $this->resqMake($response,"Token不存在", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        // 解析Token
        $payload = $this->authService->checkToken(trim($tokenArr[1]));
        if(!$payload) {
            return $this->resqMake($response,"身份验证失败，请重新登录", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        if(isset($payload[0]) && !$payload[0]) {
            return $this->resqMake($response,"Token已过期，请重新登录", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        // TODO::是否需要验证platform
//        if(in_array($payload["platform"],["*"])){
//            return $this->resqMake($response,"权限不足", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
//        }
        // 获取用户信息
        $user = $this->userService->info(trim($payload["sub"]),["id","account","nickname","mobile"]);
        if(is_null($user)){
            return $this->resqMake($response,"用户不存在", (int)$this->http_code_config["un_authorized"],(int)$this->http_code_config["un_authorized"]);
        }
        Context::set("user_info",$user);
        // 获取请求对象上下文
        return $handler->handle($request);
    }

    /**
     * 组装响应数据
     * date 2023/5/29 19:08
     * @param $response 响应对象
     * @param $e  string|object 消息体
     * @param $code http状态码
     * @param $dataCode 系统定义状态码
     * @return mixed
     */
    private function resqMake($response, $e, $code, $dataCode) {
        return $response->withHeader("Server", "merchat-api")
            ->withAddedHeader("content-type", "application/json; charset=utf-8")
            ->withStatus($code)
            ->withBody(new SwooleStream(Json::encode($this->respMessage($e, (int)$dataCode))));
    }

    /**
     * 错误信息提示
     * @param object|string $e 消息体
     * @param integer $code 系统定义状态码
     */
    public static function respMessage($e, int $code = 500): array
    {
        if(is_string($e)) {
            $message = $e;
        } else {
            $message = env("APP_ENV") == "dev" ? $e->getMessage() : "系统异常，正在处理中...";
        }
        return ["code" => $code, "message" => $message];
    }
}